/*
 * pi3's Linux kernel Runtime Guard
 *
 * Component:
 *  - Intercept 'sel_write_enforce' function
 *
 * Notes:
 *  - Intercept SELinux state modifications
 *
 * Caveats:
 *  - None
 *
 * Timeline:
 *  - Created: 13.XI.2017
 *
 * Author:
 *  - Adam 'pi3' Zabrocki (http://pi3.com.pl)
 *
 */


#ifdef CONFIG_SECURITY_SELINUX

#include "../../../../p_lkrg_main.h"

char p_sel_write_enforce_kretprobe_state = 0;

static struct kretprobe p_sel_write_enforce_kretprobe = {
    .kp.symbol_name = "sel_write_enforce",
    .handler = p_sel_write_enforce_ret,
    .entry_handler = p_sel_write_enforce_entry,
    .data_size = sizeof(struct p_sel_write_enforce_data),
    /* Probe up to 40 instances concurrently. */
    .maxactive = 40,
};

/*
 * x86-64 syscall ABI:
 *  *rax - syscall_number
 *    rdi - 1st argument
 *    rsi - 2nd argument
 *    rdx - 3rd argument
 *    rcx - 4th argument
 *
 *    r8  - 5th one
 *    r9  - 6th one
 */

int p_sel_write_enforce_entry(struct kretprobe_instance *p_ri, struct pt_regs *p_regs) {

   struct p_ed_process *p_tmp;
   unsigned long p_flags;

   p_tasks_read_lock(&p_flags);
   if ( (p_tmp = p_find_ed_by_pid(task_pid_nr(current))) != NULL) {
      // This process is on the ED list - validate 'off' flag
      p_ed_is_off_off_wrap(p_tmp);
   }
   p_tasks_read_unlock(&p_flags);

   p_ed_enforce_validation();

   // lock shadow SELinux updates
   p_lkrg_counter_lock_lock(&p_ed_guard_globals.p_selinux_lock, &p_flags);
   p_lkrg_counter_lock_val_inc(&p_ed_guard_globals.p_selinux_lock);
   p_lkrg_counter_lock_unlock(&p_ed_guard_globals.p_selinux_lock, &p_flags);

   return 0;
}


int p_sel_write_enforce_ret(struct kretprobe_instance *p_ri, struct pt_regs *p_regs) {

   // Success ?
   if (!IS_ERR((void *)p_regs_get_ret(p_regs))) {
      // track down new SELinux information
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,17,0)
      p_ed_guard_globals.p_selinux.p_selinux_state.enforcing = P_SYM(p_selinux_state)->enforcing;
 #else
      p_ed_guard_globals.p_selinux.p_selinux_enforcing = *P_SYM(p_selinux_enforcing);
 #endif
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0)
      p_ed_guard_globals.p_selinux.p_selinux_enabled = *P_SYM(p_selinux_enabled);
#endif
   }

   // unlock shadow SELinux updates
   p_lkrg_counter_lock_val_dec(&p_ed_guard_globals.p_selinux_lock);

   p_ed_enforce_validation();

   return 0;
}


GENERATE_INSTALL_FUNC(sel_write_enforce)

#endif
